/**
 * TManMiddleware.java
 * ************************** 
 * @date May 5, 2011
 * @author Michele Tamburini
 * 
 * **************************
 */
package gossipServices.bootstraping.tman;

import gossipServices.basic.nodeDescriptors.NodeDescriptor;
import gossipServices.basic.view.PartialView;
import gossipServices.basic.view.PartialView.PEER_SELECTION_ENUM;
import gossipServices.bootstraping.tman.tabuList.ConcreteTabuList;
import gossipServices.bootstraping.tman.tabuList.TabuList;
import gossipServices.gossipThread.GossipThread;
import gossipServices.network.NetworkServicesAPI;
import gossipServices.pss.PeerSamplingServiceAPI;

/**
 * Description: 
 * This class extends the GossipThread. 
 * It gather all the shared variables and method of the TMan 
 * active and passive thread such as: the view, the cycle length,
 * the ranking method, parameters 'm', 'phi', the TabuList, and
 * the variables used in the termination of the algorithm.
 *
 */
public class TManMiddleware extends GossipThread{
	
	public static final long INFINITE_WAIT_TIME = 900000000;
	
	public static final long SECOND = 1000;
	
	/*
	 * This variable is masking the shared PartialView defined
	 * in GossipThread, but we need a static pointer for TMan.
	 * 
	 * NNOOOOOOOOOO Mordor sent it to corrupt us!! never use it again!!!! ;) 
	 */	
	// DON'T CROSS THE SLASH LINE
	/////////////////////////////////////
//	protected PartialView view; /////////	
	/////////////////////////////////////
	
	
	protected static int cycles;
	
	protected PeerSamplingServiceAPI pss;
	protected NetworkServicesAPI netServ;
	protected TManRankingMethod ranker;
	protected NodeDescriptor myDescriptor;
	protected int m;
	protected int psi;
	
	// the TabuList
	protected TabuList tabuList;
	
	// variables used in the termination
	protected int deltaIdle = 10000; //ms -> from paper: 4 sec 
	protected int precViewSize;
	protected int idleCycles=0;
	protected long originalCycleLength;
	
	protected String subcloudID;
	
	
	/**
	 * @param name
	 * @param cycleLengthMsec
	 * @param tManView2
	 * @param pss2
	 * @param tmanNm
	 * @param localNodeDescriptor
	 * @param rankingMet
	 * @param messageSize
	 * @param phi
	 */
	public TManMiddleware(String name, long cycleLengthMsec,
			PartialView initView, PeerSamplingServiceAPI pss2,
			TManNetworkManager tmanNm, NodeDescriptor localNodeDescriptor,
			TManRankingMethod rankingMet, int messageSize, int phi, int tabuListSize, String subcloudID) {
		super(name, cycleLengthMsec, initView);
//		if(view == null)
//			view = inputTManView;
//		view.merge(initView);
//		view = initView;
		this.netServ = tmanNm; 
		this.myDescriptor = localNodeDescriptor;
		this.pss = pss2;
		this.ranker = rankingMet;
		this.m = messageSize;
		this.psi = phi;
		this.subcloudID = subcloudID;
		tabuList = new ConcreteTabuList(tabuListSize);
		
		//for termination:
		originalCycleLength = cycleLengthMsec;
		precViewSize = initView.size();
	}

	/**
	 * It selects a random peer from the first 'phi' peers
	 * of the inputed 'rankedView'.
	 * @param phi : int
	 * @param rankedView : PartialView
	 * @return n : NodeDescriptor
	 */
	protected NodeDescriptor selectPeer(int phi, PartialView rankedView){
		NodeDescriptor n = null;
		PartialView localView;
		if(rankedView.size() > 0){
			 localView = rankedView.head(phi);
//			 System.out.println("local view: "+localView);
			 n = localView.selectPeer(PEER_SELECTION_ENUM.rand);
//			n = rankedView.head(phi).selectPeer(PEER_SELECTION_ENUM.rand);
		}
		return n;
	}
	
	protected void debugPrint(){
		System.out.println("NODE: "+netServ.getNodeName()+" on SUBCLOUD: \""+subcloudID+"\" \n"+
				Thread.currentThread().getName() + " at cycle: "+cycles+" "+
				" current neighbors: \n" + view);
	}
	

	/**
	 * @param p
	 * @return
	 */
	protected boolean tabuListCheck(NodeDescriptor p) {
		boolean ret = (!tabuList.contains(p));
		return ret;
	}
	
	/**
	 * The actual termination check procedure simply verify if
	 * a node has been idle for 'deltaIdle'. In that case
	 * the thread which verified this condition is stopped
	 * and the node enters the state of suspend.
	 * @return
	 */
	public boolean terminationCheck(){
		int actualViewSize;
		synchronized (view) {
			actualViewSize = view.size();			
		}
		if(actualViewSize == precViewSize){
			idleCycles++;
			if(SECOND * idleCycles >= deltaIdle)
				suspendTManThread();
		}else{
			precViewSize = actualViewSize;
//			phi = actualViewSize; // always select from the entire view
			idleCycles = 0;
			activateTManThread();
		}			
		return false;
	}

	/**
	 * A node starting TMan is in the active state until it reaches
	 * the end of the algorithm, when it will be suspended.
	 * The same node may be re-activated by a new incoming node
	 * in the cloud, which need the synchronization of its view,
	 * and sends a message to the local node.
	 * 
	 */
	protected void activateTManThread() {
		System.out.println("RE-ACTIVATED TMAN "+Thread.currentThread());
		cycleLenght_msec = originalCycleLength;
	}

	/**
	 * A node starting TMan is in the active state until it reaches
	 * the end of the algorithm, when it will be suspended.
	 * This function set the actual thread to an INFINITE_WAIT_TIME 
	 * as cycleLength.
	 * 
	 */
	protected void suspendTManThread() {
		System.out.println("SUSPENDED TMAN "+Thread.currentThread());
		cycleLenght_msec = INFINITE_WAIT_TIME;
	}

	public final PartialView getView() {
		return view;
	}
		
}
